//note that in libdasm, INSTRUCTION_TYPE_JMPC and INSTRUCTION_TYPE_JECXZ are the same type 37
#include "Static.h"

POPERAND dst_op;
instruction_t *cur_instr_p;					//add by hsqfire
instruction_list_t *cur_instr_list;			//add by hsqfire
INSTRUCTION cur_inst;
INSTRUCTION tmp_inst;
INSTRUCTION next_inst;
char instr_string[256];

void StaticCompletion(CFG* fun_cfg){

	dr_fprintf(f, "Entering StaticCompletion!\n");

	//local variables anouncement
	app_pc focus_pc;
	app_pc temp_branch_to_pc;                //the pc branch point to
	app_pc temp_bb_first_instr;              //store the first instruction of current bb
	app_pc temp_bb_last_instr;               //store the last instruction of current bb
	app_pc next_bb_first_instr;              //store the first instruction of the next bb
	bool cti_met;                            //if the cti is met
	bool static_branch;                      //if is to initiate static branch procedure
	bool ret_met;                            //if ret is met
	//bool branch_finished;                  //initiate backtracking when turned on
	//int i;                                   //temp counter
	BranchList* branches_in_wait;            //branches in wait 
	basicblock_t* temp_bb;                   //temp bb
	basicblock_t* static_linked_bb;          //static last linked bb
	branch_point* temp_point;                //temp branch point 

	cti_met = false;
	static_branch = false;
	ret_met = false;

	branches_in_wait = new BranchList;	
	fun_cfg->copyBranchList(branches_in_wait);
	temp_bb = new basicblock_t;
	temp_point = new branch_point;
	temp_point = branches_in_wait->Get_head();
	if(!temp_point){
		return;
	}

	dr_fprintf(f, "Begining traversal of branch_list!\n");

	for(focus_pc = (app_pc)(fun_cfg->getUnexecutedPathAddress(temp_point->address)); focus_pc != NULL;)
	{
		if(!(static_linked_bb = fun_cfg->getBB_byAddress(temp_point->address))){
			dr_fprintf(f, "static_linked_bb not found!\n");
			return;
		}

		dr_fprintf(f, "resume in branch list node: %08x to %08x\n", temp_point->address, focus_pc);
		temp_bb_first_instr = focus_pc;
		get_instruction(&cur_inst, focus_pc, MODE_32);
		if(cur_inst.length <= 0)
			break;

		//translate the static binary code until unexcecuted instruction encountered

		/************************************************

		FIXME: note that sometimes the length of the fetched instruction is 0, I just skip it in this case. Should be investigated later. 12.29.2009

		************************************************
		*/
		cur_instr_list = create_instruction_list();	//add by hsqfire

		dr_fprintf(f, "Proceeding in instruction analysis!\n");
		while(//cur_inst.type != INSTRUCTION_TYPE_JMP &&       //it's"jmp"
			//cur_inst.type != INSTRUCTION_TYPE_JMPC &&        //it's "jmpc & jecxz"
			//cur_inst.type != INSTRUCTION_TYPE_LOOP &&        //it's "call"
			//cur_inst.type != INSTRUCTION_TYPE_CALL &&        //it's "ret"
			cur_inst.type != INSTRUCTION_TYPE_RET &&           //it's "int"
			cur_inst.type != INSTRUCTION_TYPE_ASC              //it's 0
			){
				if(static_branch)
				{
					static_branch = false;
				}

				if(cur_inst.type == INSTRUCTION_TYPE_LOOP)          //actually it's "call"
				{
					cti_met = true;
					temp_bb_last_instr = focus_pc;
					next_bb_first_instr = focus_pc + cur_inst.length;
					INSTRUCTION *cur_instr_tmp = (INSTRUCTION*)malloc(sizeof(INSTRUCTION));	//add by hsqfire
					(*cur_instr_tmp) = cur_inst;											//add by hsqfire
					cur_instr_p = create_instruction(true,(uint)focus_pc);					//add by hsqfire
					cur_instr_p->instr = cur_instr_tmp;										//add by hsqfire
					instruction_list_append(cur_instr_list,cur_instr_p);					//add by hsqfire

					get_instruction_string(&cur_inst, FORMAT_ATT, 0, instr_string, sizeof(instr_string));
					dr_fprintf(f,"static instr: 0x%08x %s\n", focus_pc, instr_string);
					//dr_fprintf(f,"cur_inst.length: %d\n", cur_inst.length);

					//dst_op = get_destination_operand(&cur_inst);

					//if(dst_op->immediate == 0 || (dst_op->immediate >= 0x8000 && dst_op->immediate <= 0xfffe8000))
					//{
					//	break;
					//}

					//focus_pc = focus_pc + cur_inst.length + dst_op->immediate;
					//dr_fprintf(f,"catch call 0x%08x\n", focus_pc);
					//get_instruction(&cur_inst, focus_pc, MODE_32);

					focus_pc = focus_pc + cur_inst.length;
					dr_fprintf(f,"catch call! resume: %08x\n", focus_pc);
					get_instruction(&cur_inst, focus_pc, MODE_32);

					if(cur_inst.length <= 0){
						dr_fprintf(f, "length<=0\n");
						break;
					}

				}//else call
				else if(cur_inst.type == INSTRUCTION_TYPE_JMP)      //actually it's "jmp"
				{
					cti_met = true;
					temp_bb_last_instr = focus_pc;
					INSTRUCTION *cur_instr_tmp = (INSTRUCTION*)malloc(sizeof(INSTRUCTION));	//add by hsqfire
					(*cur_instr_tmp) = cur_inst;											//add by hsqfire
					cur_instr_p = create_instruction(true,(uint)focus_pc);					//add by hsqfire
					cur_instr_p->instr = cur_instr_tmp;										//add by hsqfire
					instruction_list_append(cur_instr_list,cur_instr_p);					//add by hsqfire

					get_instruction_string(&cur_inst, FORMAT_ATT, 0, instr_string, sizeof(instr_string));
					dr_fprintf(f,"static instr: 0x%08x %s\n", focus_pc, instr_string);

					dst_op = get_destination_operand(&cur_inst);

					if(dst_op->immediate == 0 || (dst_op->immediate >= 0x8000 && dst_op->immediate <= 0xfffe8000)){
						dr_fprintf(f,"illegal jmp opnd!\n");
						break;
					}

					focus_pc = focus_pc + cur_inst.length + dst_op->immediate;
					next_bb_first_instr = focus_pc;
					dr_fprintf(f,"catch jmp 0x%08x\n", focus_pc);
					get_instruction(&cur_inst, focus_pc, MODE_32);

					if(cur_inst.length <= 0){
						dr_fprintf(f, "length<=0\n");
						break;
					}

				}//else jmp
				else if(cur_inst.type == INSTRUCTION_TYPE_JMPC)    //actually it's "jmpc"
				{	
					cti_met = true;
					static_branch = true;
					temp_bb_last_instr = focus_pc;
					next_bb_first_instr = focus_pc + cur_inst.length;
					INSTRUCTION *cur_instr_tmp = (INSTRUCTION*)malloc(sizeof(INSTRUCTION));	//add by hsqfire
					(*cur_instr_tmp) = cur_inst;											//add by hsqfire
					cur_instr_p = create_instruction(true,(uint)focus_pc);					//add by hsqfire
					cur_instr_p->instr = cur_instr_tmp;										//add by hsqfire
					instruction_list_append(cur_instr_list,cur_instr_p);					//add by hsqfire

					get_instruction_string(&cur_inst, FORMAT_ATT, 0, instr_string, sizeof(instr_string));
					dr_fprintf(f,"static instr: 0x%08x %s\n", focus_pc, instr_string);

					dst_op = get_destination_operand(&cur_inst);
					if(dst_op->immediate == 0 || (dst_op->immediate >= 0x8000 && dst_op->immediate <= 0xfffe8000)){
						dr_fprintf(f,"illegal jmpc opnd!\n");
						break;
					}
					temp_branch_to_pc = focus_pc + cur_inst.length + dst_op->immediate;
					
					///////////test
					branch_point* p = new branch_point;

					for(p = branches_in_wait->Get_head(); p = branches_in_wait->Get_next(); p)
					{
						dr_fprintf(f,"###%08x\n", p->address);

					}

					//////////test

					branches_in_wait->addBranchPoint((uint)focus_pc, (uint)(focus_pc + cur_inst.length), (uint)temp_branch_to_pc, false);
					dr_fprintf(f," @add branch point to CFG: %08x\n", focus_pc);
					dr_fprintf(f," temp_branch_to_pc: %08x\n", temp_branch_to_pc);

										
					///////////test
					for(p = branches_in_wait->Get_head(); p = branches_in_wait->Get_next(); p)
					{
						dr_fprintf(f,"###%08x\n", p->address);

					}

					//////////test

					focus_pc = focus_pc + cur_inst.length;
					get_instruction(&cur_inst, focus_pc, MODE_32);

					if(cur_inst.length <= 0){
						dr_fprintf(f, "length<=0\n");
						break;
					}

				}//else jmpc
				else if(cur_inst.type == INSTRUCTION_TYPE_CALL)    //actually it's "ret"
				{
					cti_met = true;
					ret_met = true;
					temp_bb_last_instr = focus_pc;
					INSTRUCTION *cur_instr_tmp = (INSTRUCTION*)malloc(sizeof(INSTRUCTION));	//add by hsqfire
					(*cur_instr_tmp) = cur_inst;											//add by hsqfire
					cur_instr_p = create_instruction(true,(uint)focus_pc);					//add by hsqfire
					cur_instr_p->instr = cur_instr_tmp;										//add by hsqfire
					instruction_list_append(cur_instr_list,cur_instr_p);					//add by hsqfire

					get_instruction_string(&cur_inst, FORMAT_ATT, 0, instr_string, sizeof(instr_string));
					dr_fprintf(f,"static instr: 0x%08x %s\n", focus_pc, instr_string);

				}//else ret
				else                                              //non-cti
				{
					cti_met = false;
					INSTRUCTION *cur_instr_tmp = (INSTRUCTION*)malloc(sizeof(INSTRUCTION));	//add by hsqfire
					(*cur_instr_tmp) = cur_inst;											//add by hsqfire
					cur_instr_p = create_instruction(true,(uint)focus_pc);					//add by hsqfire
					cur_instr_p->instr = cur_instr_tmp;										//add by hsqfire
					instruction_list_append(cur_instr_list,cur_instr_p);					//add by hsqfire

					get_instruction_string(&cur_inst, FORMAT_ATT, 0, instr_string, sizeof(instr_string));
					dr_fprintf(f,"static instr: 0x%08x %s\n", focus_pc, instr_string);
					//dr_fprintf(f,"0x%d\n", cur_inst.type);

					focus_pc = focus_pc + cur_inst.length;
					get_instruction(&cur_inst, focus_pc, MODE_32);					
					if(cur_inst.length <= 0){
						dr_fprintf(f, "length<=0\n");
						break;
					}
				}//else non_cti

				if(cti_met)
				{
					basicblock_t *newBB = create_basicblock();
					newBB->first_address = (uint)temp_bb_first_instr;
					newBB->last_address = (uint)temp_bb_last_instr;
					newBB->next_branch = NULL;
					bool in_middle = false;

					dr_fprintf(f,"temp_bb_first_instr: %08x\n", temp_bb_first_instr);

					///////// the bb already exit /////////// 
					if(temp_bb = fun_cfg->getBB_byAddress((uint)temp_bb_first_instr, &in_middle)){
						if(in_middle){
							if(temp_bb!=NULL)
							{
								if(static_linked_bb!=NULL){
									fun_cfg->linkBB(static_linked_bb,temp_bb,true);
								}
								else{
									dr_fprintf(f,"static_linked_bb = NULL!\n");
									break;
								}
							}
							else{
								dr_fprintf(f,"temp_bb = NULL\n");
							}
						}//if in_middle
						else{
							newBB = fun_cfg->addBB(newBB);
							if(newBB!=NULL){
								dr_fprintf(f," ///////////////////////split BB %d at 0x%08x to CFG\n",newBB->id,newBB->first_address);
								if(static_linked_bb!=NULL){
									fun_cfg->linkBB(static_linked_bb,newBB,true);
								}
								else{
									dr_fprintf(f,"static_linked_bb = NULL!\n");
									break;
								}
								static_linked_bb = newBB;
							}
							else{
								dr_fprintf(f," @Error when add BB to CFG\n");
								break;
							}

						}//else in_middle
													
						dr_fprintf(f,"back to analyzed part!\n");
						break;
					}
					///////// add the new BB to CFG /////////// 
					else{	
						newBB = fun_cfg->addBB(newBB);
						newBB->bb_instr_list = cur_instr_list;		//add by hsqfire
						//dr_fprintf(f,"here 6\n");	//for debug
						cur_instr_list = create_instruction_list();	//add by hsqfire
						if(newBB!=NULL){
							dr_fprintf(f," ///////////////////////add BB %d at 0x%08x to CFG\n",newBB->id,newBB->first_address);
							if(fun_cfg->getSize()==1){
								fun_cfg->setEntrance(newBB);
							}
							if(static_linked_bb!=NULL){
								fun_cfg->linkBB(static_linked_bb,newBB,true);
							}
							static_linked_bb = newBB;
						}
						else{
							dr_fprintf(f," @Error when add BB to CFG\n");
							break;
						}
					}	

					dr_fprintf(f, "temp_bb_first_instr: 0x%08x\n", temp_bb_first_instr);
					
					if(ret_met){
						dr_fprintf(f, "return met!\n");
						break;
					}

					temp_bb_first_instr = next_bb_first_instr;
				}//else cti_met
		}//static analysis ends(while)
		
		temp_point = temp_point->next;
		if( temp_point == NULL){
			dr_fprintf(f, "no branches in wait!\n");
			break;
		}

		focus_pc = (app_pc)getUnexecutedPathAddress(temp_point);
	}//traversal the branch list
	return;
}